import sys
import blue
import base
import zlib
import gc
import cPickle
import service
import weakref
import types

def CalcMemoryUsage(what, iterations = 1):
    if (session and ((session.role & (service.ROLE_PROGRAMMER | service.ROLE_SERVICE)) == 0)):
        raise RuntimeError('Requires role programmer')
    iterations = max(1, iterations)
    iterations = min(iterations, 10)
    enabled = gc.isenabled()
    if enabled:
        gc.disable()
    oldWhitelist = blue.marshal.globalsWhitelist.copy()
    blue.marshal.globalsWhitelist.clear()
    oldCollectWhitelist = blue.marshal.collectWhitelist
    blue.marshal.collectWhitelist = True
    try:
        zippedSum = pickleSum = changeSum = 0
        for i in xrange(iterations):
            pickle = blue.marshal.Save(what)
            zipped = zlib.compress(pickle)
            before = sys.getpymalloced()
            unpickled = blue.marshal.Load(pickle)
            after = sys.getpymalloced()
            pickleSum += len(pickle)
            zippedSum += len(zipped)
            changeSum += (after - before)

        denominator = float(iterations)
        return ((pickleSum / denominator),
         (zippedSum / denominator),
         (changeSum / denominator))

    finally:
        if enabled:
            gc.enable()
        blue.marshal.globalsWhitelist.clear()
        blue.marshal.globalsWhitelist.update(oldWhitelist)
        blue.marshal.collectWhitelist = oldCollectWhitelist




class RefTracker(object):
    __guid__ = 'profiling.RefTracker'

    def __init__(self):
        if not hasattr(self.__class__, 'instanceIndex'):
            self.__class__.instanceIndex = {}
        self.__class__.instanceIndex[id(self)] = weakref.ref(self)



    def __del__(self):
        del self.__class__.instanceIndex[id(self)]




def GetRefCounts():
    from nasty import nasty
    d = {}
    sys.modules
    for m in sys.modules.itervalues():
        for sym in dir(m):
            o = getattr(m, sym)
            if (type(o) is types.ClassType):
                d[o] = sys.getrefcount(o)


    for m in nasty.namespaces.itervalues():
        space = getattr(m, 'space', None)
        if (space is None):
            continue
        for o in space.itervalues():
            if (type(o) is types.ClassType):
                d[o] = sys.getrefcount(o)


    pairs = map(lambda x: (x[1], x[0]), d.items())
    pairs.sort()
    pairs.reverse()
    return pairs



def PrintRefCounts(top = 100):
    for (n, c,) in GetRefCounts()[:top]:
        print ('%10d %s' % (n, c.__name__))




def __GetReferrers(classType, depth = 4, instance = None):
    import gc
    depth -= 1
    ret = {}
    if (depth > 0):
        referrers = gc.get_referrers((classType or instance))
        for o in referrers:
            if (classType is None):
                ret[str(instance)] = __GetReferrers(None, depth, o)
            else:
                if isinstance(o, classType):
                    ret[classType] = __GetReferrers(None, depth, o)

    return ret



def Test(classType):
    from nasty import nasty
    d = {}
    sys.modules
    for m in sys.modules.itervalues():
        for sym in dir(m):
            o = getattr(m, sym)
            if (type(o) is types.ClassType):
                if (o != classType):
                    pass
            referrers = __GetReferrers(o)
            d[o] = referrers
            continue


    for m in nasty.namespaces.itervalues():
        space = getattr(m, 'space', None)
        if (space is None):
            continue
        for o in space.itervalues():
            if (type(o) is types.ClassType):
                if (o != classType):
                    pass
            referrers = __GetReferrers(o)
            d[o] = referrers
            continue


    return d



def CheckSM():
    from service import ROLE_SERVICE
    ignorableForROLE_SERVICE = ['genderID',
     'bloodlineID',
     'languageID',
     'rolesAtHQ',
     'wingid',
     'constellationid',
     'regionid',
     'fleetid',
     'rolesAtAll',
     'rolesAtBase',
     'hqID',
     'locationid',
     'shipid',
     'rolesAtOther',
     'squadid',
     'raceID',
     'gangrole',
     'solarsystemid',
     'charid',
     'corprole',
     'corpid',
     'userid',
     'stationid',
     'allianceid',
     'solarsystemid2']
    print '\nChecking sm.services for leaked sessions\n'
    for (serviceName, serviceObject,) in sm.services.iteritems():
        if not hasattr(serviceObject, 'boundObjects'):
            continue
        if (0 == len(serviceObject.boundObjects)):
            continue
        print ('Checking: %s' % serviceName)
        for boundObject in serviceObject.boundObjects.itervalues():
            print '\n    ',
            print str(boundObject),
            print ('\n    ' + ('=' * len(str(boundObject))))
            print '    Object Connections:'
            for obj in boundObject.objectConnections.itervalues():
                if obj.__session__:
                    print '        __session__ roles: ',
                    print obj.__session__.role
                print '        ',
                print obj.__dict__

            print '    Session Connections:'
            for sess in boundObject.sessionConnections.itervalues():
                print '        session roles: ',
                print sess.role
                for (k, v,) in sess.__dict__.iteritems():
                    if (k == 'connectedObjects'):
                        print '        ',
                        print k,
                        print ':'
                        for (someID, connection,) in v.iteritems():
                            print '            ',
                            print someID,
                            print ':',
                            print connection

                    else:
                        if (sess.role & ROLE_SERVICE):
                            if (k in ignorableForROLE_SERVICE):
                                continue
                        print '        ',
                        print k,
                        print ': ',
                        print v



        print



exports = {'profiling.CalcMemoryUsage': CalcMemoryUsage,
 'profiling.GetRefCounts': GetRefCounts,
 'profiling.PrintRefCounts': PrintRefCounts,
 'profiling.Test': Test,
 'profiling.Test2': __GetReferrers,
 'profiling.CheckSM': CheckSM}

